#include "File.h"
#include <Windows.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")

namespace bl{

File::File(void) : file_(INVALID_HANDLE_VALUE)
{
}

File::~File(void)
{
	if(file_ != INVALID_HANDLE_VALUE)
		Close();
}

bool File::Exists(const tchar* fileName)
{
	return 0 != ::PathFileExists(fileName);
}

bool File::Delete(const tchar* fileName)
{
	if(!Exists(fileName))
		return true;
	return 0 != ::DeleteFile(fileName);
}

int64 File::GetFileSize(const tchar* fileName)
{
	WIN32_FILE_ATTRIBUTE_DATA data;
	::ZeroMemory(&data, sizeof(data));
	
	if(!::GetFileAttributesEx(fileName, ::GetFileExInfoStandard, &data) )
		return -1;

	LARGE_INTEGER size;
	size.LowPart = data.nFileSizeLow;
	size.HighPart = data.nFileSizeHigh;
	
	return size.QuadPart;
}

bool File::Open(const tchar* fileName, OpenAccessMode accessMode, OpenShareMode shareMode, ExistsMode existsMode)
{
	DWORD desired_access = 0;
	switch(accessMode){
	case READ:
		desired_access = GENERIC_READ;
		break;
	case WRITE:
		desired_access = GENERIC_WRITE;
		break;
	case  READ_WRITE:
		desired_access = GENERIC_READ | GENERIC_WRITE;
		break;
	}

	DWORD share_mode = 0;
	switch(shareMode){
		case SHARE_NONE:
			share_mode = 0;
			break;
		case SHARE_READ:
			share_mode = FILE_SHARE_READ;
			break;
		case SHARE_WRITE:
			share_mode = FILE_SHARE_WRITE;
			break;
		case SHARE_READ_WRITE:
			share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE;
			break;
		case SHARE_DELETE:
			share_mode = FILE_SHARE_DELETE;
			break;
	}

	DWORD creation_disposition = 0;
	switch(existsMode){
	case NEVER_FAIL:
		creation_disposition = (accessMode==READ) ? OPEN_ALWAYS : CREATE_ALWAYS;
		break;
	case FAIL_IF_NOT_EXISTS:
		creation_disposition = OPEN_EXISTING;
		break;
	case FAIL_IF_EXISTS:
		if(accessMode == READ)
			return false;
		creation_disposition = CREATE_NEW;
		break;
	}

	file_ = CreateFile(fileName, desired_access, share_mode, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);

	return INVALID_HANDLE_VALUE != file_;
}

bool File::OpenRead(const tchar* fileName)
{
	return Open(fileName, READ, SHARE_READ_WRITE, FAIL_IF_NOT_EXISTS);
}

bool File::OpenWrite(const tchar* fileName)
{
	return Open(fileName, WRITE, SHARE_READ, FAIL_IF_EXISTS);
}

bool File::Read(uint8* buf, const uint32 len, uint32* bytesRead)
{
	DWORD bytes_read = 0;
	if( !::ReadFile(file_, buf, len, &bytes_read, NULL))
		return false;

	if(bytesRead)
		*bytesRead = bytes_read;

	return true;
}

bool File::Write(const uint8* buf, const uint32 len, uint32* bytesWritten)
{
	DWORD wrote = 0;
	if( !::WriteFile(file_, buf, len, &wrote, NULL) )
		return false;

	if(bytesWritten)
		*bytesWritten = wrote;

	return len==wrote;
}

bool File::Flush()
{
	return ::FlushFileBuffers(file_) != 0;
}

bool File::Seek(int64 offset, SeekMethod seekMethod /* = SEEK_FROM_START */) const
{
	DWORD move_method = 0;
	switch(seekMethod)
	{
	case SEEK_FROM_START:
		move_method = FILE_BEGIN;
		break;
	case SEEK_FROM_CURRENT:
		move_method = FILE_CURRENT;
		break;
	case SEEK_FROM_END:
		move_method = FILE_END;
		break;
	}

	LARGE_INTEGER pos;
	pos.QuadPart = offset;
	return !!::SetFilePointerEx(file_, pos, NULL, move_method);
}

int64 File::Position() const
{
	LARGE_INTEGER zero;
	zero.QuadPart = 0;
	LARGE_INTEGER pos;
	pos.QuadPart = 0;
	return ::SetFilePointerEx(file_, zero, &pos, FILE_CURRENT) ? pos.QuadPart : -1;
}


int64 File::Size() const
{
	LARGE_INTEGER size;
	return ::GetFileSizeEx(file_, &size) ? size.QuadPart : -1;
}

bool File::Truncate(int64 len)
{
	if(len<0)
		return false;

	if(!Seek(len, SEEK_FROM_START))
		return false;

	return !!::SetEndOfFile(file_);
}

bool File::Close()
{
	bool ret = !!::CloseHandle(file_);
	file_ = INVALID_HANDLE_VALUE;
	return ret;
}


}	// End of BL